package com.zsc.oj.zojcodesendbox.judge;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.dfa.FoundWord;
import cn.hutool.dfa.WordTree;
import com.zsc.oj.zojcodesendbox.utils.ProcessUtils;
import com.zsc.oj.zojcodesendbox.utils.model.ExecuteMessage;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * java原生代码沙箱
 */

public class JavaNativeCodeSendBoxDisable implements CodeSendBox{


    public static void main(String[] args) {
        JavaNativeCodeSendBoxDisable javaNativeCodeSendBox = new JavaNativeCodeSendBoxDisable();
        ExecuteCodeRequest executeCodeRequest = new ExecuteCodeRequest();
        //读取代码
        String code = ResourceUtil.readStr("test/Main.java", StandardCharsets.UTF_8);
        executeCodeRequest.setCode(code);
        executeCodeRequest.setLanguage("java");
        executeCodeRequest.setInputList(Arrays.asList("1 2","1 3"));
        ExecuteCodeResponse executeCodeResponse = javaNativeCodeSendBox.ExecuteCode(executeCodeRequest);
        System.out.println(executeCodeResponse);
//        try {
//            Process process = Runtime.getRuntime().exec("java -version");
//            System.out.println(process.toString());
//        } catch (IOException e) {
//            throw new RuntimeException(e);
//        }

    }
    public static final String GLOBAL_CODE_DIR_NAME = "tempCode";

    public static final String FILE_NAME = "Main.java";

    //创建黑名单，一旦用户提交的代码存在以下词就直接返回
    public static final List<String> BACK_LIST = Arrays.asList("Files","exec");


    @Override
    public ExecuteCodeResponse ExecuteCode(ExecuteCodeRequest executeCodeRequest) {

        //开启Security Manager
//        System.setSecurityManager(new DefaultSecurityManager());

        String code = executeCodeRequest.getCode();

        //创建一颗字典树
        WordTree wordTree = new WordTree();
        //将黑名单存入字典树
        wordTree.addWords(BACK_LIST);
        FoundWord foundWord = wordTree.matchWord(code);
        //如果用户提交的代码和黑名单的词匹配则直接返回
        if(foundWord != null){
            System.out.println("包含敏感词"+foundWord.getFoundWord());
            return null;
        }


        //1、把用户代码保存为文件

        //定位到项目的根目录
        String RootUrl = System.getProperty("user.dir");
        //在根目录下创建一个tempCode文件夹用来存放用户代码
        String globalPathName = RootUrl + File.separator + GLOBAL_CODE_DIR_NAME;

        //如果tempCode文件夹不存在，则创建一个
        if(!FileUtil.exist(globalPathName)){
            FileUtil.mkdir(globalPathName);
        }

        //通过UUID把用户的代码隔离存放，因为每个用户提交的代码都是main函数
        String codeParentPathName = globalPathName + File.separator + UUID.randomUUID();
        //将用户代码文件写入tempCode文件夹中
        File file = FileUtil.writeString(code, codeParentPathName + File.separator + FILE_NAME, StandardCharsets.UTF_8);

        //2、编译代码

        //定义命令行语句
        String compileCode = String.format("javac -encoding utf-8 %s",file.getAbsolutePath());
        try {
            //执行命令行语句
            Process process = Runtime.getRuntime().exec(compileCode);
            //调用自定义工具类查看编译信息
            ExecuteMessage executeMessage = ProcessUtils.getProcessMessage(process,"编译");
            System.out.println(executeMessage);
        } catch (Exception e) {
            return getErrorResponse(e);
        }

        //3、运行代码

        //获取输入用例
        List<String> inputList = executeCodeRequest.getInputList();
        //收集所以的运行信息
        List<ExecuteMessage> executeMessageList = new ArrayList<>();
        for(String input : inputList){
            //定义命令行语句
            String runCmd = String.format("java -Dfile.encoding=UTF-8 -cp %s Main %s",codeParentPathName,input);
            try {
                //执行命令行语句
                Process process = Runtime.getRuntime().exec(runCmd);
                //调用自定义工具类查看编译信息
                ExecuteMessage executeMessage = ProcessUtils.getProcessMessage(process, "运行");
                System.out.println(executeMessage);
                executeMessageList.add(executeMessage);
            } catch (Exception e) {
                return getErrorResponse(e);
            }
        }

        //4、收集整理输出结果

        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        List<String> outputList = new ArrayList<>();
        //选用最大值，便于判断是否超时
        long maxTime = 0;
        for (ExecuteMessage executeMessage : executeMessageList) {
            //如果运行时发生错误
            String errorMessage = executeMessage.getErrorMessage();
            if(StrUtil.isNotBlank(errorMessage)){
                executeCodeResponse.setMessage(errorMessage);
                executeCodeResponse.setStatus(3);
                break;
            }
            //将运行的正确信息设置到outputList
            outputList.add(executeMessage.getSuccessMessage());
            //超时时间
            Long time = executeMessage.getTime();
            if(time != null){
                maxTime = Math.max(maxTime,time);
            }
        }
        if(outputList.size() == executeMessageList.size()){
            executeCodeResponse.setStatus(1);
        }

        executeCodeResponse.setOutputList(outputList);

        JudgeInfo judgeInfo = new JudgeInfo();
        judgeInfo.setTime(maxTime);
        executeCodeResponse.setJudgeInfo(judgeInfo);

        //5、清理文件

       if(file.getParent() != null){
           boolean del = FileUtil.del(file);
           if(del){
               System.out.println("删除成功");
           }else{
               System.out.println("删除失败");
           }
       }

        return executeCodeResponse;
    }

    /**
     * 获取错误码
     * @param e
     * @return
     */
    private ExecuteCodeResponse getErrorResponse(Throwable e){
        ExecuteCodeResponse executeCodeResponse = new ExecuteCodeResponse();
        executeCodeResponse.setOutputList(new ArrayList<>());
        executeCodeResponse.setMessage(e.getMessage());
        //2 表示代码沙箱错误
        executeCodeResponse.setStatus(2);
        executeCodeResponse.setJudgeInfo(new JudgeInfo());
        return executeCodeResponse;
    }
}
